package com.wnxy.shop.sqlprovider;

import org.apache.ibatis.jdbc.SQL;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author song 2019年11月30日18:22:52
 * <p>
 *     工具人
 * SQL provider的 父接口，
 * 提供公共方法 default方法
 * *****************************************************
 *              仅用于输出入语句，copy子类方法中
 *              因为实体类往往有很多不属于数据库列的属性
 * *****************************************************
 */
public interface SqlProvider {
    /**
     * 获取传入实体的非空属性
     *
     * @param entity
     * @param <T>
     * @return field name list
     * @throws IllegalAccessException
     */
    default <T> List<String> getNotNullField(T entity) throws IllegalAccessException
    {
        List<Field> fields = getFields(entity);
        List<String> list = new ArrayList<>();
        for (Field field : fields) {
            // 判空
            field.setAccessible(true);
            if (field.get(entity) != null) {
                list.add(field.getName());
            }
        }
        return list;
    }

    /**
     * 获取对象的field 数组
     *
     * @param entity
     * @param <T>
     * @return
     */
    default <T> List<Field> getFields(T entity)
    {
        Class clazz = entity.getClass();
        return Arrays.asList(clazz.getDeclaredFields());
    }

    /**
     * 获取对象的，fields string，如果对象中包含数据库不存在字段，记得去除
     * eg:
     *      传入user，属性有：id，name，age，password
     *      返回字符串："id,name,age,password"
     *
     * @param entity
     * @param <T>
     * @return
     */
    default <T> String getColumns(T entity)
    {
        StringBuilder sb = new StringBuilder();
        getFields(entity).forEach(f -> sb.append(f.getName() + ","));
        // 去除最后一个多余逗号
        return sb.substring(0, sb.length() - 1);
    }

    /**
     * 获取mybatis插入语句，如果对象中包含数据库不存在字段，记得去除
     * eg：
     *      传入user，属性有：id，name，age，password
     *      返回字符串："#{id},#{name},#{age},#{password}"
     * @param entity
     * @param <T>
     * @return
     */
    default <T> String getValues(T entity)
    {
        StringBuilder sb = new StringBuilder();
        getFields(entity).forEach(f -> sb.append("#{" + f.getName() + "},"));
        // 去除最后一个多余逗号
        return sb.substring(0, sb.length() - 1);
    }

    /**
     * 获取 provider插入语句的方法题语句
     * eg：
     *      return new SQL() {
     *             {
     *                 INSERT_INTO("shop_user");
     *                 VALUES("id,name,password,phoneNum,money,avatar,regTime,score" +
     *                                 ",lastLoginIp,lastLoginTime,status"
     *                         , "#{id},#{name},#{password},#{phoneNum},#{money},#{avatar},#{regTime},#{score}"
     *                                 + ",#{lastLoginIp},#{lastLoginTime},#{status}");
     *             }
     *         }.toString();
     *
     * @param entity
     * @param <T>
     * @return
     */
    default <T> String getInsertMethodString(T entity)
    {
        String tableName = getTableName(entity);
        StringBuilder sb = new StringBuilder();
        // 第一行
        sb.append("\r\n\t return new SQL(){\r\n");
        sb.append("\t\t { \r\n");
        sb.append("\t\t\t INSERT_INTO(\"" + tableName + "\"); \r\n");
        sb.append("\t\t\t VALUES(\"" + getColumns(entity) + "\",\t \r\n");
        sb.append("\t\t\t\t\t \"" + getValues(entity) + "\"); \r\n");
        sb.append("\t\t } \r\n");
        sb.append("\t}.toString(); \r\n");

        return sb.toString();
    }

    /**
     * 根据实体获取其表名
     * @param entity
     * @param <T>
     * @return
     */
    default <T> String getTableName(T entity)
    {
        String tableName = entity.getClass().getName().toLowerCase();
        tableName = "shop_" + tableName.substring(tableName.lastIndexOf(".") + 1);
        return tableName;
    }

    /**
     * 获取mapper的SQL语句，用于控制台输出copy到dao注解中，不使用provider
     * eg:
     *      "insert into shop_user values" +
     *             "(#{id},#{name},#{password},#{phoneNum},#{money},#{avatar}," +
     *             "#{regTime},#{score},#{lastLoginIp},#{lastLoginTime},#{status})"
     * @param entity
     * @param <T>
     * @return
     */
    default <T> String getInsertString(T entity)
    {
        return new SQL() {
            {
                INSERT_INTO(getTableName(entity));
                VALUES(getColumns(entity), getValues(entity));
            }
        }.toString();

    }

    /**
     * 获取@Update注解中语句
     * @param entity
     * @param <T>
     * @return
     */
    default <T> String getUpdateString(T entity)
    {
        StringBuilder sb = new StringBuilder();
        sb.append("{\"<script>\",\r\n");
        sb.append("\"update " + getTableName(entity) + "\", \r\n");
        sb.append("\"\t <set>\", \r\n");
        getFields(entity).forEach(f -> {
            sb.append("\"\t\t <if test='" + f.getName() + " != null'> " + f.getName() + " =#{" + f.getName() + "}," +
                    "</if>" +
                    "\",\r\n ");
        });
        sb.append("\"\t </set>\", \r\n ");
        sb.append("\"where id=#{id}\",\r\n");
        sb.append("\"</script>\"}\r\n");
        return sb.toString();
    }

    /**
     * 获取@Select注解中语句
     * @param entity
     * @param <T>
     * @return
     */
    default <T> String getQueryString(T entity)
    {
        StringBuilder sb = new StringBuilder();
        sb.append("{\"<script>\",\r\n");
        sb.append("\"select * from " + getTableName(entity) + "\", \r\n");
        sb.append("\"\t <where>\", \r\n");
        getFields(entity).forEach(f -> {
            sb.append("\"\t\t <if test='" + f.getName() + " != null'> and " + f.getName() + "=#{" + f.getName() + "} " +
                    "</if>" +
                    "\",\r\n ");
        });
        sb.append("\"</where>\",\r\n");
        sb.append("\"</script>\"}\r\n");
        return sb.toString();
    }

}
